function JSortDialog(owner, parent, name, caption, action, method, rect, previous)
{
	var self = this,
		_rendered = false,
		_disposed = false,
		_loadCnt = 0,
		_ids = [name + '_al', name + '_ar', name + '_dl', name + '_dr', name + '_au', name + '_ad'],
		_dragHandler = null;

	this.initialize(owner, parent, name, caption, action, rect);

	this.isForm   = true;
	this.method   = method;
	this.previous = previous;
	this.methods  = (cmRender | cmLoad);
	this.columns  = null;
	this.confirm  = null;
	this.cancel   = null;
	this.selected = [];

	this.owner.addImage("close-icon", "close.gif", "form");
	this.owner.addImage("checkbox-up",		"checkbox-up.gif",        "controls");
	this.owner.addImage("checkbox-down",		"checkbox-down.gif",      "controls");
	this.owner.addImage("arrow-up",			"arrow-up.gif",           "controls");
	this.owner.addImage("arrow-down",		"arrow-down.gif",         "controls");
	this.owner.addImage("arrow-left",		"arrow-left.gif",         "controls");
	this.owner.addImage("arrow-right", 		"arrow-right.gif",        "controls");
	this.owner.addImage("arrow-double-left",	"arrow-double-left.gif",  "controls");
	this.owner.addImage("arrow-double-right",	"arrow-double-right.gif", "controls");

	this.render = render;
	this.load   = load;
	this.dispose = dispose;
	this.close  = close;
	this.click  = click;
	this.select = select;
	this.toggle = toggle;
	this.isRendered = isRendered;

	this.rect.normalize(this.owner.getDocument().body);

	this.owner.activeForm = this;


	function render(fs)
	{
		if (_rendered)
			return;

		var rect0 = new JRect(utils.format("%1;%2;%3;%4", 5, 10, Math.floor((self.rect.width - 50) / 2), self.rect.height - 80));
		var rect1 = new JRect(utils.format("%1;%2;%3;%4", rect0.left + rect0.width + 5, rect0.top + 20, 15, rect0.height - 40));
		var rect2 = new JRect(utils.format("%1;%2;%3;%4", rect1.left + rect1.width + 15, rect0.top, rect0.width, rect0.height));
		var rect3 = utils.format("%1;%2;%3;%4", self.rect.width - 155, self.rect.height - 60, 60, 24);
		var rect4 = utils.format("%1;%2;%3;%4", self.rect.width -  85, self.rect.height - 60, 60, 24);
		var previous = self.owner.getObject(self.previous);
		var prevDS = previous.dataset;
		if (previous.activeControl != null)
			prevDS = previous.activeControl.dataset;

		/* Confirm & Cancel buttons */
		if ((self.confirm == null) && (self.cancel == null))
		{
			self.confirm = self.owner.registerObject(self.name + "_confirm", new JButton(self.owner, self.name, self.name + "_confirm", self.owner.localize("capOK"),     true, rect3, raCloseParent, null));
			self.cancel  = self.owner.registerObject(self.name + "_cancel",  new JButton(self.owner, self.name, self.name + "_cancel",  self.owner.localize("capCancel"), true, rect4, raCloseParent, null));
		}

		/* Column list */
		var data = null;
		if (self.columns == null)
		{
			self.columns = self.owner.registerObject(self.name + "_columns_local", new JDataset(self.owner, self.name, self.name + "_columns_local", "", "", dfNone));
			self.columns.addField(new JField("fldid", "", ftString,  "",             ffNone));
			self.columns.addField(new JField("fldlb", "", ftString,  "",             ffNone));
			self.columns.addField(new JField("fldor", "", ftEnum,    "0=ASC;1=DESC", ffNone));
			self.columns.addField(new JField("fldsl", "", ftInteger, "",             ffNone));
			/* Load */
			var fields = prevDS.fields;

			fields.forIn( function (field, fldName)
			{
				if  (! utils.inSet(ffHidden, field.flags))
				{
					data = "fldid=" + fldName + "%17" + "fldlb=" + field.caption + "%17" + "fldor=0%17fldsl=0%17"
					self.columns.loadRecord("add", fldName, data, rsUnchanged, true);
				}
			}, self);

			self.sortedColumns = 0;

			if (prevDS.sort != "")
			{
				var prevSort  = prevDS.sort.split(";");
				var row   = null;
				var parts = null;
				prevSort.forEach( function(sort, pair)
				{
					parts = sort.split(" ");
					row   = self.columns.findByKey(parts[0]);
					if (row != null)
					{
						self.columns.gotoRow(row);
						self.columns.set("fldor", (parts[1] == "ASC" ? 0 : 1));
						self.columns.set("fldsl", parseInt(pair) + 1);
					}
				}, self);
				self.sortedColumns = prevSort.length;
			}
			self.columns.gotoRow(0);
		}

		fs.append( '<div class="dialogForm" id="' + self.name + '" style="' + self.rect + '">');
		fs.append('  <div id="' + self.name + '_title" class="form_titlebar">');
		fs.append('    <span id="' + self.name + '_caption" class="form_caption">' + self.caption + '</span>');
		fs.append('  </div>');

		fs.append(   '<div class="dialogForm_inlet" id="' + self.name + '_inlet" style="top: ' + (fmDragbarHeight + fmBorderWidth) + 'px; height: ' + (self.rect.height - fmDragbarHeight - 3 * fmBorderWidth) + 'px; width: ' + (self.rect.width - 2 * fmBorderWidth) + 'px">');
		fs.append(     '<div style="position: absolute; ' + rect0 + '; background-color: window; border-style: inset; border-width: 2px; overflow-x: auto; overflow-y: scroll">');
		fs.append(       '<table id="' + self.name + '_lpane" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; overflow: scroll; width: 100%">');
		fs.append(       '</table>');
		fs.append(     '</div>');
		fs.append(     '<div style="position: absolute; ' + rect1 + '; border-style: none; border-width: 1px">');
		fs.append(       '<table border="0">');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_al" hspace="3" src="' + self.owner.images["arrow-left"].src         + '" style="cursor: hand"></td></tr>');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_ar" hspace="3" src="' + self.owner.images["arrow-right"].src        + '" style="cursor: hand"></td></tr>');
		fs.append(         '<tr><td>&nbsp;</td></tr>');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_dl" hspace="3" src="' + self.owner.images["arrow-double-left"].src  + '" style="cursor: hand"></td></tr>');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_dr" hspace="3" src="' + self.owner.images["arrow-double-right"].src + '" style="cursor: hand"></td></tr>');
		fs.append(         '<tr><td>&nbsp;</td></tr>');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_au" hspace="3" src="' + self.owner.images["arrow-up"].src           + '" style="cursor: hand"></td></tr>');
		fs.append(         '<tr><td align="left"><img id="' + self.name + '_ad" hspace="3" src="' + self.owner.images["arrow-down"].src         + '" style="cursor: hand"></td></tr>');
		fs.append(       '</table>');
		fs.append(     '</div>');
		fs.append(     '<div style="position: absolute; ' + rect2 + '; background-color: window; border-style: inset; border-width: 2px; overflow-x: auto; overflow-y: scroll">');
		fs.append(       '<table id="' + self.name + '_rpane" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; overflow: scroll; width: 100%">');
		fs.append(       '</table>');
		fs.append(     '</div>');

		self.confirm.render(fs);
		self.cancel.render(fs);

		fs.append(   "</div>");
		fs.append( "</div>");

		_rendered = true;
	}

	function load()
	{
		_loadCnt++;

		var lpane = self.$$('_lpane');
		var rpane = self.$$('_rpane');

		if (_loadCnt == 1)
		{
			self.owner.getDocument().body.appendChild(self.$$());


			lpane.onclick = rpane.onclick = self.select;
			lpane.ondblclick = rpane.ondblclick = self.click;

			self.confirm.load();
			self.cancel.load();

			for (var i = 0; i < _ids.length; i++)
			{
				self.$(_ids[i]).onclick = self.click;
			}

			self.owner.splash(true);
			self.owner.setSplashInfo('');
		}

		/* Clear */
		while (lpane.rows.length > 0)
		{
			lpane.deleteRow(0);
		}
		while (rpane.rows.length > 0)
		{
			rpane.rows[0].cells[1].onmousedown = null;
			rpane.deleteRow(0);
		}

		var pane = null;
		var row  = null;
		var cell = null;
		var i = 0;
		var sel = utils.coalesce(self.selected[rpane.id], -2);

		for (i = 0; i < self.sortedColumns; i++)
			rpane.insertRow(-1);

		self.columns.records.forEach( function (item, i)
		{
			var sortIndex = self.columns.get("fldsl", i);
			pane = (sortIndex == 0) ? lpane : rpane;
			if (pane == rpane)
				row = pane.rows[parseInt(sortIndex) - 1];
			else
				row = pane.insertRow(-1);

			row.id = ((pane == lpane) ? "l_" : "r_") + i;
			row.height = 20;

			row.style.backgroundColor = "window";
			row.style.color = "windowtext";
			if ((pane == lpane) && (sel == -2))
			{
				self.selected[lpane.id] = 0;
				self.selected[rpane.id] = null;
				row.style.backgroundColor = "highlight";
				row.style.color = "window";
				sel = -1;
			}

			cell = row.insertCell(-1);
			cell.className = "sortDialogItem";
			cell.noWrap = true;
			cell.innerHTML = self.columns.getText("fldlb", i);

			if (pane == rpane)
			{
				cell = row.insertCell(-1);
				cell.className = "sortDialogItem";
				cell.noWrap = true;
				cell.width = 1;
				cell.innerHTML = '<img id="' + self.name + '_ord_' + i + '" src="' + self.owner.images[(self.columns.get("fldor", i) == 0) ? "checkbox-up" : "checkbox-down"].src + '" style="cursor: hand; cursor: pointer">';
				self.$$('_ord_' + i).onmousedown = self.click;
			}
		}, self);

		if ((self.sortedColumns > 0) && (sel > -1))
		{
			if (self.selected[rpane.id] == null)
				self.selected[rpane.id] = 0;
			rpane.rows[self.selected[rpane.id]].style.backgroundColor = "highlight";
			rpane.rows[self.selected[rpane.id]].style.color = "window";
			self.selected[lpane.id] = null;
		}

		if (typeof(DragHandler) != 'undefined')
			_dragHandler = new DragHandler(self.$$(), self.$$('_title'));
	}

	function isRendered()
	{
	  return _rendered;
	}

	function close(e)
	{
		self.owner.splash(false);
		self.$$().style.display = 'none';

		var element = utils.getEventElement(e);
		while (element.className != "button")
		{
			element = element.parentNode;
		}

		switch (element.id)
		{
			case self.name + "_confirm" :
				self.$$().style.borderStyle = "outset";
				var orderBy = new Array(self.sortedColumns);

				self.columns.records.forEach( function(item, i)
				{
					var sortIndex = parseInt(self.columns.get("fldsl", i));
					if (sortIndex > 0)
					{
						orderBy[sortIndex - 1] = utils.format('"%1" %2', self.columns.get("fldid", i), self.columns.getText("fldor", i));
					}
				}, self);
				var afmobj = self.owner.activeForm;
				var pfmobj = self.owner.getObject(self.previous);
				var pfmDS = pfmobj.dataset;
				if (pfmobj.activeControl != null)
					pfmDS = pfmobj.activeControl.dataset; 
				var query  = new Postback();

				query.set("dat", escape(orderBy.join(";")));
				query.set("rec", pfmDS.getCurrentRecord().key);
				query.set("pfm", pfmobj.name);
				query.set("pds", pfmDS.name);

				self.owner.post(raSetSort, query);
				break;

			case self.name + "_cancel" :
//				self.$$().style.borderStyle = "outset";
				self.owner.unregisterObject(self.name);
				self.dispose();
				break;
		}
	}

	function click(e)
	{
		var element = utils.getEventElement(e);
		if (_ids.indexOf(element.id) > -1)
		{
			switch (_ids.indexOf(element.id))
			{
				case 0 :
					if (self.sortedColumns > 0)
						self.toggle(self.name + "_rpane");
					break;

				case 2 :
					if (self.sortedColumns > 0)
						self.toggle(self.name + "_rpane", true);
					break;

				case 1 :
					if (self.sortedColumns < self.columns.records.length)
						self.toggle(self.name + "_lpane");
					break;

				case 3 :
					if (self.sortedColumns < self.columns.records.length)
						self.toggle(self.name + "_lpane", true);
					break;

				case 4 :
					if ((self.selected[self.name + "_rpane"] == null) || (parseInt(self.selected[self.name + "_rpane"]) == 0))
						return;

					var pane = self.$$("_rpane");
					var selIndex = parseInt(self.selected[self.name + "_rpane"]);

					if (pane.rows[selIndex] == null)
						break;

					pane.rows[selIndex].style.backgroundColor = "window";
					pane.rows[selIndex].style.color = "windowtext";

					self.columns.records.forEach( function(item, i)
					{
						self.columns.gotoRow(parseInt(i));
						var sortIndex = parseInt(self.columns.get("fldsl", i));
						if (sortIndex == selIndex)
						{
							self.columns.set("fldsl", sortIndex + 1);        // move previous down
						}
						else
						if (sortIndex == selIndex + 1)
							self.columns.set("fldsl", sortIndex - 1);        // move selected up
					}, self);

					selIndex--;
					self.selected[self.name + "_rpane"] = selIndex;

					self.load();

					pane.rows[selIndex].style.backgroundColor = "highlight";
					pane.rows[selIndex].style.color = "window";
					break;

				case 5 :
					if ((self.selected[self.name + "_rpane"] == null) || (parseInt(self.selected[self.name + "_rpane"]) >= self.sortedColumns - 1))
						return;

					var pane = self.$$("_rpane");
					var selIndex = parseInt(self.selected[self.name + "_rpane"]);

					if (pane.rows[selIndex] == null)
						break;

					pane.rows[selIndex].style.backgroundColor = "window";
					pane.rows[selIndex].style.color = "windowtext";

					self.columns.records.forEach( function(item, i)
					{
						self.columns.gotoRow(i);
						var sortIndex = parseInt(self.columns.get("fldsl", i));
						if (sortIndex == selIndex + 2)
						{
							self.columns.set("fldsl", sortIndex - 1);        // move previous down
						}
						else
						if (sortIndex == selIndex + 1)
							self.columns.set("fldsl", sortIndex + 1);        // move selected up
					}, self);

					selIndex++;
					self.selected[self.name + "_rpane"] = selIndex;

					self.load();

					pane.rows[selIndex].style.backgroundColor = "highlight";
					pane.rows[selIndex].style.color = "window";
					break;
			}
			return;
		}
		else
		if (element.className == "sortDialogItem")
		{
			var pane = element;
			while (pane.tagName != "TABLE")
				pane = pane.parentNode;

			var paneName = pane.id.split("_")[1];
			if ((paneName == 'lpane') && (self.sortedColumns < self.columns.records.length))
				self.toggle(self.name + "_lpane");
			else
			if ((paneName == 'rpane') && (self.sortedColumns > 0))
				self.toggle(self.name + "_rpane");

		}

		var id = element.id.split("_");
		if (id[1] == "ord")
		{
			self.columns.gotoRow(parseInt(id[2]));
			self.columns.set("fldor", (1 - self.columns.get("fldor")));
			element.src = self.owner.images[(self.columns.get("fldor") == 0) ? "checkbox-up" : "checkbox-down"].src
		}
		//self.toggle();
	}

	function select(e)
	{
		element = utils.getEventElement(e);

		/* Clear previous selection */
		var pane = element;
		while (pane.tagName != "TABLE")
			pane = pane.parentNode;

		var oppane = (pane.id.split("_")[1] == "lpane") ? self.$$("_rpane") : self.$$("_lpane");
		if (pane.rows[self.selected[pane.id]] != null)
		{
			pane.rows[self.selected[pane.id]].style.backgroundColor = "window";
			pane.rows[self.selected[pane.id]].style.color = "windowtext";
		}

		/* Set new selection */
		var row = element;
		while (row.tagName != "TR")
		{
			row = row.parentNode;
		}

		self.selected[pane.id] = row.rowIndex;
		pane.rows[self.selected[pane.id]].style.backgroundColor = "highlight";
		pane.rows[self.selected[pane.id]].style.color = "window";

		/* Clear selection in opposite pane */
		if ((self.selected[oppane.id] != null) && (oppane.rows[self.selected[oppane.id]] != null))
		{
			oppane.rows[self.selected[oppane.id]].style.backgroundColor = "window";
			oppane.rows[self.selected[oppane.id]].style.color = "windowtext";
			self.selected[oppane.id] = null;
		}
	}

	function toggle(srcPane, all)
	{
		var pane = self.$(srcPane);
		if (pane == null)
			return;

		if ((all != null) && (all == true))
		{
			self.columns.records.forEach( function(item, i)
			{
				self.columns.gotoRow(i);
				if ((srcPane == self.name + "_lpane") && (self.columns.get("fldsl") == 0))
				{
					self.sortedColumns++;
					self.columns.set("fldsl", self.sortedColumns);
				}
				if ((srcPane == self.name + "_rpane") && (self.columns.get("fldsl") != 0))
				{
					self.sortedColumns--;
					self.columns.set("fldsl", 0);
				}
			}, self);

			var oppane = (pane.id.split("_")[1] == "lpane") ? self.$$("_rpane") : self.$$("_lpane");

			self.load();

			if (self.selected[pane.id] != null)
				self.selected[oppane.id] = self.selected[pane.id];

			self.selected[pane.id] = null;
			oppane.rows[self.selected[oppane.id]].style.backgroundColor = "highlight";
			oppane.rows[self.selected[oppane.id]].style.color = "window";
			//      self.select(oppane);
		}
		else
		if ((self.selected[pane.id] != null) && (pane.rows[self.selected[pane.id]] != null))
		{
			self.columns.gotoRow(parseInt(pane.rows[self.selected[pane.id]].id.split("_")[1]));
			if (srcPane == self.name + "_lpane")
			{
				self.sortedColumns++;
				self.columns.set("fldsl", self.sortedColumns);
				self.selected[pane.id] = null
				self.selected[self.name + "_rpane"] = self.sortedColumns - 1;
			}
			else
			{
				self.columns.set("fldsl", 0);
				for (var  i = parseInt(self.selected[pane.id]) + 1; i < self.sortedColumns; i++)
				{
					self.columns.gotoRow(parseInt(pane.rows[i].id.split("_")[1]));
					self.columns.set("fldsl", self.columns.get("fldsl") - 1);
				}
				self.sortedColumns--;
				if (self.columns.getRecCount() - self.sortedColumns > 0)
				{
					self.selected[pane.id] = null;
					self.selected[self.name + "_lpane"] = self.columns.getRecCount() - self.sortedColumns;
				}
			}

			self.load();
		}
	}

	function dispose()
	{
		if (_disposed)
			return;

		var lpane = self.$$('_lpane'),
			rpane = self.$$('_rpane');

		if (_dragHandler != null)
			_dragHandler.dispose();

		lpane.onclick = rpane.onclick = null;
		lpane.ondblclick = rpane.ondblclick = null;

		for (var i = 0; i < _ids.length; i++)
		{
			self.$(_ids[i]).onclick = null;
		}

		self.columns.records.forEach( function (item, i)
		{
			if (self.$$('_ord_' + i) != null)
				self.$$('_ord_' + i).onmousedown = null;
		}, self);

		self.owner.unregisterObject(self.name + "_confirm");
		self.owner.unregisterObject(self.name + "_cancel");
		self.confirm.dispose();
		self.cancel.dispose();

		if (self.$$().parentNode != null)
			self.$$().parentNode.removeChild(self.$$());

		self.base.dispose.call(self);
		_disposed = true;
	}
}

JSortDialog.inheritsFrom(ControlBase);

if (typeof(loadNextScript) != 'undefined')
	loadNextScript();